#include <sstream>
#include <ftl/either_trans.h>
#include <ftl/functional.h>

#ifndef PARSER_GEN_H
#define PARSER_GEN_H

/**
 * Error reporting class.
 * 
 * We could have used a string directly, but this thin wrapper conveys more
 * semantic information to users of the library.
 */
class error {
public:
	// Default versions of c-tors default, copy, and move are acceptable

	/// Construct from string error message
	explicit error(const std::string& msg) noexcept : e(msg) {}
	explicit error(std::string&& msg) noexcept : e(std::move(msg)) {}

	/// Access the error message
	std::string message() const noexcept {
		return e;
	}

private:
	std::string e;
};

namespace ftl {
	template<>
	struct monoid<error> {
		static error id() {
			return error(std::string(""));
		}

		static error append(const error& e1, const error& e2) {
			if(e1.message().empty())
				return error(e2.message());
			if(e2.message().empty())
				return error(e1.message());

			return error(e1.message() + " or " + e2.message());
		}

		static constexpr bool instance = true;
	};
}

/// Convenience function to reduce template gibberish
template<typename T>
ftl::either<error,T> fail(const std::string& s) {
	return ftl::make_left<T>(error(s));
}

/// Convenience function to reduce template gibberish
template<typename T>
auto yield(T&& t) -> decltype(ftl::make_right<error>(std::forward<T>(t))) {
	return ftl::make_right<error>(std::forward<T>(t));
}

/**
 * A parser of Ts.
 *
 * This is the central data type of the library.
 *
 * \par Concepts
 * \li Monad 
 * \li MonoidAlternative
 */
template<typename T>
using parser = ftl::eitherT<error,ftl::function<T(std::istream&)>>;

/**
 * Function for running parsers.
 */
template<typename T>
ftl::either<error,T> run(parser<T> p, std::istream& is) {
	return (*p)(is);
}

/* What follows is a basic set of blocks that a user of the library can
 * combine with the various combinators available (operator||, monad instance,
 * applicative instance, functor instance).
 */

/**
 * Parses any one character.
 *
 * This parser can only fail if the end of stream has been reached.
 */
parser<char> anyChar();

/**
 * Parses one specific character.
 *
 * This parser will fail if the next character in the stream is not equal
 * to \c c.
 */
parser<char> parseChar(char c);

/**
 * Parses any character except c.
 *
 * This parser will fail if the next character \em does equal \c c.
 */
parser<char> notChar(char c);

/**
 * Parses one of the characters in str.
 *
 * This parser will fail if the next character in the stream does not appear
 * in str.
 */
parser<char> oneOf(std::string str);

/**
 * Greedily parses 0 or more of p.
 *
 * This parser cannot fail. If end of stream is reached or p fails on the
 * first run, the result will be an empty string.
 */
parser<std::string> many(parser<char> p);

/**
 * Greedily parses 1 or more of p.
 *
 * This parser will fail if the first attempt at parsing p fails.
 */
parser<std::string> many1(parser<char> p);

/**
 * Lazily run the parser generated by f
 *
 * This is useful e.g. if you want a parser to recurse.
 */
template<typename T>
parser<T> lazy(ftl::function<parser<T>()> f) {
	return parser<T>([f](std::istream& is) {
		return (*f())(is);
	});
}

/// \overload
template<typename T>
parser<T> lazy(parser<T>(*f)()) {
	return parser<T>([f](std::istream& is) {
			return (*f())(is);
	});
}

#endif

